﻿using System;

namespace Lightray.Hattrick.ChppLib.EntityTypes
{
    public class WebFetchingScheduler
    {
        public struct DataTimes
        {
            public DateTime MyTeam { get; set; }
            public DateTime MyCountry { get; set; }
            public DateTime MyLeagueAndTeams { get; set; }
            public DateTime MyPlayers { get; set; }
            public DateTime MyMatches { get; set; }
            public DateTime MyRegion { get; set; }
            public DateTime Live { get; set; }
            public DateTime MyEconomy { get; set; }
            public DateTime MyLastMatch { get; set; }
        };

        private readonly TimeSpan ADD_RANDOM_SPAN = TimeSpan.FromMinutes(10);
        private readonly TimeSpan TIME_AFTER_TRAINING = TimeSpan.FromMinutes(30);
        private readonly TimeSpan TIME_AFTER_ECONOMY = TimeSpan.FromMinutes(30);
        private readonly TimeSpan TIME_AFTER_MATCH = TimeSpan.FromMinutes(15);
        private readonly TimeSpan PRE_GAME_TIME = TimeSpan.FromMinutes(30);
        private readonly TimeSpan FETCH_PLAYERS_MAX_INTERVAL = TimeSpan.FromHours(24);
        private readonly TimeSpan FETCH_ECONOMY_MAX_INTERVAL = TimeSpan.FromDays(7);
        private readonly TimeSpan FETCH_COUNTRY_MAX_INTERVAL = TimeSpan.FromDays(7);
        private readonly TimeSpan FETCH_LEAGUE_MAX_INTERVAL = TimeSpan.FromDays(7);
        private readonly TimeSpan FETCH_MATCHES_MAX_INTERVAL = TimeSpan.FromDays(7);
        private readonly TimeSpan FETCH_MATCHES_MIN_INTERVAL = TimeSpan.FromMinutes(5);
        private readonly TimeSpan FETCH_REGION_MAX_INTERVAL = TimeSpan.FromDays(7);
        private readonly TimeSpan FETCH_TEAM_MAX_INTERVAL = TimeSpan.FromDays(7);

        private Random _random = new Random();
        private bool _gotCountryDates;
        private bool _isInMatch = false;
        private bool _gotPreGame = false;
        private DateTime _nextEconomyTime;
        private DateTime _nextTrainingTime;
        private DateTime _matchEndTime = DateTime.MinValue;
        private DateTime _lastMatchesRequest = DateTime.MinValue;
        private int _upcomingMatchRequestID = 0;

        private TimeSpan getRandomTimespan(TimeSpan maxVal)
        {
            return TimeSpan.FromSeconds(_random.Next((int)maxVal.TotalSeconds));
        }

        public WebFetchingScheduler(LeagueCountry country)
        {
            if (country == null)
            {
                _nextEconomyTime = DateTime.MinValue;
                _nextTrainingTime = DateTime.MinValue;
                _gotCountryDates = false;
            }
            else
            {
                _nextEconomyTime = country.EconomyDate + TIME_AFTER_ECONOMY + getRandomTimespan(ADD_RANDOM_SPAN);
                _nextTrainingTime = country.TrainingDate + TIME_AFTER_TRAINING + getRandomTimespan(ADD_RANDOM_SPAN);
                _gotCountryDates = true;
            }
        }

        public WebFetchingScheduler():this(null) { }        

        public FetchRequest GetSchedule(DateTime hattrickTime, DataTimes dataTimes, Match upcomingMatch)
        {
            FetchRequest toFetch = FetchRequest.Nothing;

            if (!_gotCountryDates)
                return FetchRequest.MyTeamFully;

            toFetch |= checkMaxInterval(hattrickTime, dataTimes);

            toFetch |= preGameHandling(hattrickTime, upcomingMatch);

            toFetch |= matchHandling(hattrickTime, dataTimes, upcomingMatch);

            toFetch |= timedFetches(hattrickTime, dataTimes);

            return toFetch;
        }

        private FetchRequest timedFetches(DateTime hattrickTime, DataTimes dataUpdateTimes)
        {
            FetchRequest toFetch = FetchRequest.Nothing;

            if ((hattrickTime > _nextEconomyTime) && (dataUpdateTimes.MyEconomy < _nextEconomyTime))
                toFetch = toFetch | FetchRequest.MyEconomy;

            if ((hattrickTime > _nextTrainingTime) && (dataUpdateTimes.MyPlayers < _nextTrainingTime))
                toFetch = toFetch | FetchRequest.MyPlayers;

            return FetchRequest.Nothing;
        }

        private FetchRequest matchHandling(DateTime hattrickTime, DataTimes dataUpdateTimes, Match upcomingMatch)
        {
            FetchRequest toFetch = FetchRequest.Nothing;
            if (upcomingMatch == null)
                return FetchRequest.Nothing;

            if (_upcomingMatchRequestID != upcomingMatch.ID)
            {
                toFetch |= FetchRequest.MyLastMatchDeatils;
                _lastMatchesRequest = hattrickTime;
                _upcomingMatchRequestID = upcomingMatch.ID;
            }

            if (_lastMatchesRequest > dataUpdateTimes.MyLastMatch)
            {
                toFetch |= FetchRequest.MyLastMatchDeatils;
            }

            //if ((hattrickTime > upcomingMatch.MatchDate) && (upcomingMatch.Status == MatchStatus.Upcoming) && (hattrickTime > _lastMatchesRequest + FETCH_MATCHES_MIN_INTERVAL + getRandomTimespan(ADD_RANDOM_SPAN)))
            if ((hattrickTime > upcomingMatch.MatchDate) && (upcomingMatch.Status == MatchStatus.Upcoming))
            {
                toFetch = toFetch | FetchRequest.MyMatches;
                _lastMatchesRequest = hattrickTime;
            }

            if (upcomingMatch.Status == MatchStatus.Ongoing)
            {
                toFetch = toFetch | FetchRequest.Live;
                _isInMatch = true;
            }
            else
            {
                if (_isInMatch)
                {
                    _matchEndTime = hattrickTime + TIME_AFTER_MATCH + getRandomTimespan(ADD_RANDOM_SPAN);
                    _isInMatch = false;
                }
                if ((hattrickTime > _matchEndTime) && (_matchEndTime != DateTime.MinValue))
                {
                    if (_matchEndTime < dataUpdateTimes.MyLastMatch)
                        _matchEndTime = DateTime.MinValue;
                    else
                    {
                        toFetch = toFetch | FetchRequest.MyLastMatchDeatils;
                        _lastMatchesRequest = hattrickTime;
                        _upcomingMatchRequestID = upcomingMatch.ID;
                    }

                    _gotPreGame = false;
                }
            }

            return toFetch;
        }

        public void UpdateDownloadTimes(LeagueCountry country)
        {
            if (country == null)
            {
                _nextEconomyTime = DateTime.MinValue;
                _nextTrainingTime = DateTime.MinValue;
                _gotCountryDates = false;
            }
            else
            {
                _nextEconomyTime = country.EconomyDate + TIME_AFTER_ECONOMY + getRandomTimespan(ADD_RANDOM_SPAN);
                _nextTrainingTime = country.TrainingDate + TIME_AFTER_TRAINING + getRandomTimespan(ADD_RANDOM_SPAN);
                _gotCountryDates = true;
            }
        }     

        private FetchRequest preGameHandling(DateTime hattrickTime, Match upcomingMatch)
        {
            if (upcomingMatch == null || _gotPreGame)
                return FetchRequest.Nothing;

            if (hattrickTime > upcomingMatch.MatchDate - (PRE_GAME_TIME + getRandomTimespan(ADD_RANDOM_SPAN)))
            {
                _gotPreGame = true;
                return FetchRequest.MyOpponentDetails;
            }

            return FetchRequest.Nothing;
        }

        private FetchRequest checkMaxInterval(DateTime hattrickTime, DataTimes dataUpdateTimes)
        {
            FetchRequest toFetch = FetchRequest.Nothing;

            if (hattrickTime > dataUpdateTimes.MyPlayers + FETCH_PLAYERS_MAX_INTERVAL + getRandomTimespan(ADD_RANDOM_SPAN))
                toFetch = toFetch | FetchRequest.MyPlayers;

            if (hattrickTime > dataUpdateTimes.MyEconomy + FETCH_ECONOMY_MAX_INTERVAL + getRandomTimespan(ADD_RANDOM_SPAN))
                toFetch = toFetch | FetchRequest.MyEconomy;

            if (hattrickTime > dataUpdateTimes.MyCountry + FETCH_COUNTRY_MAX_INTERVAL + getRandomTimespan(ADD_RANDOM_SPAN))
                toFetch = toFetch | FetchRequest.MyCountry;

            if (hattrickTime > dataUpdateTimes.MyLeagueAndTeams + FETCH_LEAGUE_MAX_INTERVAL + getRandomTimespan(ADD_RANDOM_SPAN))
                toFetch = toFetch | FetchRequest.MyLeague | FetchRequest.MyLeagueOtherTeams | FetchRequest.MyTeam;

            if (hattrickTime > dataUpdateTimes.MyMatches + FETCH_MATCHES_MAX_INTERVAL + getRandomTimespan(ADD_RANDOM_SPAN))
                toFetch = toFetch | FetchRequest.MyMatches;

            if (hattrickTime > dataUpdateTimes.MyRegion + FETCH_REGION_MAX_INTERVAL + getRandomTimespan(ADD_RANDOM_SPAN))
                toFetch = toFetch | FetchRequest.MyRegion;

            if (hattrickTime > dataUpdateTimes.MyTeam + FETCH_TEAM_MAX_INTERVAL + getRandomTimespan(ADD_RANDOM_SPAN))
                toFetch = toFetch | FetchRequest.MyTeam;

            return toFetch;
        }        
    }
}
